//
//  Copyright (C) 1998-2007 J. Andrew McLaughlin
// 
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU General Public License as published by the Free
//  Software Foundation; either version 2 of the License, or (at your option)
//  any later version.
// 
//  This program is distributed in the hope that it will be useful, but
//  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
//  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
//  for more details.
//  
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

// This file contains the C functions belonging to the kernel's 
// multitasker

#include "kernelMultitasker.h"
#include "kernelDebug.h"
#include "kernelEnvironment.h"
#include "kernelError.h"
#include "kernelFile.h"
#include "kernelInterrupt.h"
#include "kernelLog.h"
#include "kernelMain.h"
#include "kernelMalloc.h"
#include "kernelMemory.h"
#include "kernelMisc.h"
#include "kernelNetwork.h"
#include "kernelPage.h"
#include "kernelParameters.h"
#include "kernelPerformanceCounter.h"
#include "kernelPic.h"
#include "kernelProcessorLevel.h"
#include "kernelProcessorX86.h"
#include "kernelRandom.h"
#include "kernelShutdown.h"
#include "kernelSysTimer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#define PROC_KILLABLE(proc) ((proc != kernelProc) &&        \
                             (proc != exceptionProc) &&     \
                             (proc != idleProc) &&          \
                             (proc != kernelCurrentProcess))

#define SET_PORT_BIT(bitmap, port) \
do { bitmap[port / 8] |=  (1 << (port % 8)); } while (0)
#define UNSET_PORT_BIT(bitmap, port) \
do { bitmap[port / 8] &= ~(1 << (port % 8)); } while (0)
#define GET_PORT_BIT(bitmap, port) ((bitmap[port / 8] >> (port % 8)) & 0x01)
  
// Global multitasker stuff
static int multitaskingEnabled = 0;
static volatile int processIdCounter = KERNELPROCID;
static kernelProcess *kernelProc = NULL;
static kernelProcess *idleProc = NULL;
static kernelProcess *exceptionProc = NULL;
static volatile int processingException = 0;
static volatile unsigned exceptionAddress = 0;
static kernelProcess *fpuProcess = NULL;
static volatile int inScheduler = 0;

// We allow the pointer to the current process to be exported, so that
// when a process uses system calls, there is an easy way for the
// process to get information about itself.
kernelProcess *kernelCurrentProcess = NULL;

// Process queue for CPU execution
static kernelProcess *processQueue[MAX_PROCESSES];
static volatile int numQueued = 0;

/****************************************************************************/

// Things specific to the scheduler.  The scheduler process is just a
// convenient place to keep things, we don't use all of it and it doesn't
// go in the queue
static kernelProcess *schedulerProc = NULL;
static volatile int schedulerStop = 0;

/****************************************************************************/

static int pcDispatchCalls = -1;
static int pcDispatchYieldCalls = -1;
static int pcDispatchAsyncCalls = -1;
static int pcDispatchRescheduleCalls = -1;
static int pcContextSwitches = -1;
static int pcWaitCalls = -1;
static int pcBlockCalls = -1;
static int pcProcessesCreated = -1;
static int pcFpuExceptions = -1;
static int pcFpuContextSaved = -1;
static int pcFpuContextRestored = -1;

/****************************************************************************/

// An array of exception types.  The selectors are initialized later.
static struct {
  int index;
  kernelSelector tssSelector;
  const char *a;
  const char *name;
  int (*handler) (void);

} exceptionVector[19] = {
  { EXCEPTION_DIVBYZERO, 0, "a", "divide-by-zero", NULL },
  { EXCEPTION_DEBUG, 0, "a", "debug", NULL },
  { EXCEPTION_NMI, 0, "a", "non-maskable interrupt (NMI)", NULL },
  { EXCEPTION_BREAK, 0, "a", "breakpoint", NULL },
  { EXCEPTION_OVERFLOW, 0, "a", "overflow", NULL },
  { EXCEPTION_BOUNDS, 0, "a", "out-of-bounds", NULL },
  { EXCEPTION_OPCODE, 0, "an", "invalid opcode", NULL },
  { EXCEPTION_DEVNOTAVAIL, 0, "a", "device not available", NULL },
  { EXCEPTION_DOUBLEFAULT, 0, "a", "double-fault", NULL },
  { EXCEPTION_COPROCOVER, 0, "a", "co-processor segment overrun", NULL },
  { EXCEPTION_INVALIDTSS, 0, "an", "invalid TSS", NULL },
  { EXCEPTION_SEGNOTPRES, 0, "a", "segment not present", NULL },
  { EXCEPTION_STACK, 0, "a", "stack", NULL },
  { EXCEPTION_GENPROTECT, 0, "a", "general protection", NULL },
  { EXCEPTION_PAGE, 0, "a", "page fault", NULL },
  { EXCEPTION_RESERVED, 0, "a", "\"reserved\"", NULL },
  { EXCEPTION_FLOAT, 0, "a", "floating point", NULL },
  { EXCEPTION_ALIGNCHECK, 0, "an", "alignment check", NULL },
  { EXCEPTION_MACHCHECK, 0, "a", "machine check", NULL }
};

/****************************************************************************/

#if 1
#define ACQUIRE_LOCK_PROCESS_QUEUE() int interrupts1 = 0; kernelProcessorSuspendInts(interrupts1);
#define RELEASE_LOCK_PROCESS_QUEUE() kernelProcessorRestoreInts(interrupts1);
#define ASSERT_LOCK_PROCESS_QUEUE() int interrupts2 = 0; kernelProcessorIntStatus(interrupts2); if (interrupts2) kernelPanic("ASSERT_LOCK_PROCESS_QUEUE()");
#else
#define ACQUIRE_LOCK_PROCESS_QUEUE()
#define RELEASE_LOCK_PROCESS_QUEUE()
#define ASSERT_LOCK_PROCESS_QUEUE()
#endif

/****************************************************************************/

static kernelProcess *getProcessById(int processId)
{
	// This routine is used to find a process' pointer based on the process
	// Id.  Nothing fancy -- it just searches through the list.  Maybe later
	// it can be some kind of fancy sorting/searching procedure.  Returns NULL
	// if the process doesn't exist

	kernelProcess *theProcess = NULL;
	int count;

	ACQUIRE_LOCK_PROCESS_QUEUE();

	for (count = 0; count < numQueued; count ++)
	{
		if (!processQueue[count])
			continue;
		if (processQueue[count]->processId == processId)
		{
			theProcess = processQueue[count];
			break;
		}
	}

	RELEASE_LOCK_PROCESS_QUEUE();

	// If we didn't find it, this will still be NULL
	return (theProcess);
}

/****************************************************************************/

static kernelProcess *getProcessByName(const char *name)
{
	// As above, but searches by name

	kernelProcess *theProcess = NULL;
	int count;

	ACQUIRE_LOCK_PROCESS_QUEUE();

	for (count = 0; count < numQueued; count ++)
	{
		if (!processQueue[count])
			continue;
		if (!strcmp((char *) processQueue[count]->processName, name))
		{
			theProcess = processQueue[count];
			break;
		}
	}

	RELEASE_LOCK_PROCESS_QUEUE();

	// If we didn't find it, this will still be NULL
	return (theProcess);
}

/****************************************************************************/

#include "kernelMultitasker_Create.c"

__attribute__((noreturn))
static void exceptionHandler(void)
{
	// This code is the general exception handler.  Before multitasking starts,
	// it will be called as a function in the exception context (the context
	// of the process that experienced the exception).  After multitasking
	// starts, it is a separate kernel thread that sleeps until woken up.

	char message[256];
	char *symbolName = NULL;
	int interrupt = 0;
	int count;
  
	extern kernelSymbol *kernelSymbols;
	extern int kernelNumberSymbols;

	while (1)
	{
		// We got an exception.

		if (multitaskingEnabled)
		{
			if (kernelCurrentProcess == NULL)
				// We have to make an error here.  We can't return to the program
				// that caused the exception, and we can't tell the multitasker
				// to kill it.  We'd better make a kernel panic.
				kernelPanic("Exception handler unable to determine current process");
			else
				kernelCurrentProcess->state = proc_stopped;
		}

		// If the fault occurred while we were processing an interrupt,
		// we should tell the PIC that the interrupt service routine is
		// finished.  It's not really fair to kill a process because an
		// interrupt handler is screwy, but that's what we have to do for
		// the time being.
		if (kernelProcessingInterrupt)
			kernelPicEndOfInterrupt(0xFF);
		else if (!multitaskingEnabled || (kernelCurrentProcess == kernelProc))
			sprintf(message, "The kernel has experienced %s %s exception",
				exceptionVector[processingException].a,
				exceptionVector[processingException].name);
		else
			sprintf(message, "Process \"%s\" caused %s %s exception",
				kernelCurrentProcess->processName,
				exceptionVector[processingException].a,
				exceptionVector[processingException].name);

		if (exceptionAddress >= KERNEL_VIRTUAL_ADDRESS)
		{
			if (kernelSymbols)
			{
				// Find roughly the kernel function where the exception
				// happened
				for (count = 0; count < kernelNumberSymbols; count ++)
				{
					if ((exceptionAddress >= kernelSymbols[count].address)
					&&  (exceptionAddress < kernelSymbols[count + 1].address))
					{
						symbolName = kernelSymbols[count].symbol;
						break;
					}
				}
			}

			if (symbolName)
				sprintf((message + strlen(message)), " in function %s (%08x)",
					symbolName, exceptionAddress);
			else
				sprintf((message + strlen(message)), " at kernel address %08x",
					exceptionAddress);
		}
		else
			sprintf((message + strlen(message)), " at application address %08x",
				exceptionAddress);

		if (kernelProcessingInterrupt)
		{
			interrupt = kernelPicGetActive();
			if (interrupt >= 0)
				sprintf((message + strlen(message)), " while processing interrupt %d",
					interrupt);
		}

		if (!multitaskingEnabled || (kernelCurrentProcess == kernelProc))
		{
			// If it's the kernel, we're finished
			kernelPanic(message);
		}
		else
		{
			kernelError(kernel_error, message);

			// If we're in graphics mode, make an error dialog (but don't get
			// into an endless loop if the crashed process was an error dialog
			// thread itself).
			if (kernelGraphicsAreEnabled()
			&&  strcmp((char *) kernelCurrentProcess->processName, ERRORDIALOG_THREADNAME))
				kernelErrorDialog("Application Exception", message);
		}

      /*
      // If the process was in kernel code do a stack trace
      if (!kernelProcessingInterrupt && (address >= KERNEL_VIRTUAL_ADDRESS))
	kernelStackTrace((kernelCurrentProcess->userStack +
			  ((void *) kernelCurrentProcess
			   ->taskStateSegment.ESP -
			   kernelCurrentProcess->userStack)),
			 (kernelCurrentProcess->userStack +
			  kernelCurrentProcess->userStackSize -
			  sizeof(void *)));
      */

		// The scheduler may now dismantle the process
		kernelCurrentProcess->state = proc_finished;

		kernelProcessingInterrupt = 0;
		processingException = 0;
		exceptionAddress = 0;

		// Yield the timeslice back to the scheduler.  The scheduler will take
		// care of dismantling the process
		kernelMultitaskerYield();
	}
}


static int spawnExceptionThread(void)
{
  // This function will initialize the kernel's exception handler thread.  
  // It should be called after multitasking has been initialized.  

  int status = 0;
  int procId = 0;

  // Create the kernel's exception handler thread.
  procId =
    kernelMultitaskerSpawn(&exceptionHandler, "exception thread", 0, NULL);
  if (procId < 0)
    return (status = procId);

  exceptionProc = getProcessById(procId);
  if (exceptionProc == NULL)
    return (status = ERR_NOCREATE);

  // Set the process state to sleep
  exceptionProc->state = proc_sleeping;

  status = kernelDescriptorSet(
	       exceptionProc->tssSelector, // TSS selector
	       &(exceptionProc->taskStateSegment), // Starts at...
	       sizeof(kernelTSS),      // Maximum size of a TSS selector
	       1,                      // Present in memory
	       PRIVILEGE_SUPERVISOR,   // Highest privilege level
	       0,                      // TSS's are system segs
	       0x9,                    // TSS, 32-bit, non-busy
	       0,                      // 0 for SMALL size granularity
	       0);                     // Must be 0 in TSS
  if (status < 0)
    // Something went wrong
    return (status);

  // Interrupts should always be disabled for this task 
  exceptionProc->taskStateSegment.EFLAGS = 0x00000002;

  return (status = 0);
}

#include "kernelMultitasker_IdleThread.c"

static int markTaskBusy(int tssSelector, int busy)
{
  // This function gets the requested TSS selector from the GDT and
  // marks it as busy/not busy.  Returns negative on error.
  
  int status = 0;
  kernelDescriptor descriptor;
  
  // Initialize our empty descriptor
  kernelMemClear(&descriptor, sizeof(kernelDescriptor));

  // Fill out our descriptor with data from the "official" one that 
  // corresponds to the selector we were given
  status = kernelDescriptorGet(tssSelector, &descriptor);
  if (status < 0)
    return (status);

  // Ok, now we can change the selector in the table
  if (busy)
    descriptor.attributes1 |= 0x00000002;
  else
    descriptor.attributes1 &= ~0x00000002;
    
  // Re-set the descriptor in the GDT
  status =  kernelDescriptorSetUnformatted(tssSelector, 
   descriptor.segSizeByte1, descriptor.segSizeByte2, descriptor.baseAddress1,
   descriptor.baseAddress2, descriptor.baseAddress3, descriptor.attributes1, 
   descriptor.attributes2, descriptor.baseAddress4);
  if (status < 0)
    return (status);

  // Return success
  return (status = 0);
}

/****************************************************************************/

#include "kernelMultitasker_Dispatcher.c"

/****************************************************************************/

static void kernelProcess2Process(kernelProcess *kernProcess,
				  process *userProcess)
{
  // Given a kernel-space process structure, create the corresponding
  // user-space version.
  
  strncpy(userProcess->processName, (char *) kernProcess->processName,
	  MAX_PROCNAME_LENGTH);
  userProcess->userId = kernProcess->userId;
  userProcess->processId = kernProcess->processId;
  userProcess->type = kernProcess->type;
  userProcess->priority = kernProcess->priority;
  userProcess->privilege = kernProcess->privilege;
  userProcess->parentProcessId = kernProcess->parentProcessId;
  userProcess->descendentThreads = kernProcess->descendentThreads;
  userProcess->cpuPercent = kernProcess->cpuPercent;
  userProcess->state = kernProcess->state;
}


static int fpuExceptionHandler(void)
{
	// This function gets called when a EXCEPTION_DEVNOTAVAIL (7) exception
	// occurs.  It can happen under two circumstances:
	// CR0[EM] is set: No FPU is present.  We can implement emulation here
	//     later in this case, if we want.
	// CR0[TS] and CR0[MP] are set: A task switch has occurred since the
	//     last FP operation, and we need to restore the state.

	int status = 0;
	unsigned short fpuReg = 0;

	kernelDebug(debug_multitasker, "FPU exception start");

	kernelPerformanceCounterInc1(pcFpuExceptions);

	kernelProcessorClearTaskSwitched();

	if (fpuProcess && (fpuProcess == kernelCurrentProcess))
	{
		// This was the last process to use the FPU.  The state should be the
		// same as it was, so there's nothing to do.
		kernelDebug(debug_multitasker, "FPU exception end: nothing to do");
		return (status = 0);
	}

	kernelProcessorGetFpuStatus(fpuReg);
	while (fpuReg & 0x8000)
	{
		kernelDebugError("FPU is busy");
		kernelProcessorGetFpuStatus(fpuReg);
	}

	// Save the FPU state for the previous process
	if (fpuProcess)
	{
		// Save FPU state
		kernelDebug(debug_multitasker, "Switch FPU ownership from %s to %s", fpuProcess->name, kernelCurrentProcess->name);
		kernelDebug(debug_multitasker, "Save FPU state for %s", fpuProcess->name);
		if (!fpuProcess->fpuState)
			fpuProcess->fpuState = kernelMalloc(FPU_STATE_LEN);
		if (!fpuProcess->fpuState)
		{
			kernelError(kernel_error, "Failed to allocate fpuState buffer for process %u", kernelCurrentProcess->processId);
			return (status = ERR_MEMORY);
		}
		kernelProcessorFpuStateSave(fpuProcess->fpuState[0]);
		fpuProcess->fpuStateSaved = 1;
		kernelPerformanceCounterInc1(pcFpuContextSaved);
	}

	if (kernelCurrentProcess->fpuStateSaved)
	{
		// Restore the FPU state
		kernelDebug(debug_multitasker, "Restore FPU state for %s", kernelCurrentProcess->name);
		if (!kernelCurrentProcess->fpuState)
			kernelPanic("Failed restore FPU state. kernelCurrentProcess->fpuState == NULL");
		kernelProcessorFpuStateRestore(kernelCurrentProcess->fpuState[0]);
		kernelPerformanceCounterInc1(pcFpuContextRestored);
	}
	else
	{
		// No saved state for the FPU.  Initialize it.
		kernelDebug(debug_multitasker, "Initialize FPU for %s", kernelCurrentProcess->name);
		kernelProcessorFpuInit();
		kernelProcessorGetFpuControl(fpuReg);
		// Mask FPU exceptions.
		fpuReg |= 0x3F;
		kernelProcessorSetFpuControl(fpuReg);
	}

	kernelCurrentProcess->fpuStateSaved = 0;

	kernelProcessorFpuClearEx();

	fpuProcess = kernelCurrentProcess;

	kernelDebug(debug_multitasker, "FPU exception end");
	return (status = 0);

}


/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
//  Below here, the functions are exported for external use
//
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////


int kernelMultitaskerInitialize(void)
{
  // This function intializes the kernel's multitasker.

  int status = 0;
  unsigned cr0 = 0;
  
  // Make sure multitasking is NOT enabled already
  if (multitaskingEnabled)
    return (status = ERR_ALREADY);

  // Now we must initialize the process queue
  kernelMemClear(processQueue, (MAX_PROCESSES * sizeof(kernelProcess *)));
  numQueued = 0;

  // Initialize the CPU for floating point operation.  We set
  // CR0[EM]=0 (no emulation)
  // CR0[MP]=1 (math present)
  // CR0[NE]=1 (floating point errors cause exceptions)
  kernelProcessorGetCR0(cr0);
  cr0 = ((cr0 & ~0x04UL) | 0x22);
  kernelProcessorSetCR0(cr0);

  // We need to create the kernel's own process.
  status = createKernelProcess();
  // Make sure it was successful
  if (status < 0)
    return (status);

  // Now start the scheduler
  status = schedulerInitialize();
  if (status < 0)
    // The scheduler couldn't start
    return (status);

  // Create an "idle" thread to consume all unused cycles
  status = spawnIdleThread();
  // Make sure it was successful
  if (status < 0)
    return (status);

  // Set up any specific exception handlers.
  exceptionVector[EXCEPTION_DEVNOTAVAIL].handler = fpuExceptionHandler;

  // Start the exception handler thread.
  status = spawnExceptionThread();
  // Make sure it was successful
  if (status < 0)
    return (status);

  // Log a boot message
  kernelLog("Multitasking started");

  pcDispatchCalls           = kernelPerformanceCounterRegister("multitasker.dispatch_calls");
  pcDispatchYieldCalls      = kernelPerformanceCounterRegister("multitasker.dispatch_yield_calls");
  pcDispatchAsyncCalls      = kernelPerformanceCounterRegister("multitasker.dispatch_async_calls");
  pcDispatchRescheduleCalls = kernelPerformanceCounterRegister("multitasker.dispatch_reschedule_calls");
  pcContextSwitches         = kernelPerformanceCounterRegister("multitasker.context_switches");
  pcWaitCalls               = kernelPerformanceCounterRegister("multitasker.wait_calls");
  pcBlockCalls              = kernelPerformanceCounterRegister("multitasker.block_calls");
  pcProcessesCreated        = kernelPerformanceCounterRegister("multitasker.processes_created");
  pcFpuExceptions           = kernelPerformanceCounterRegister("multitasker.fpu_exceptions");
  pcFpuContextSaved         = kernelPerformanceCounterRegister("multitasker.fpu_context_saved");
  pcFpuContextRestored      = kernelPerformanceCounterRegister("multitasker.fpu_context_restored");

  // Return success
  return (status = 0);
}


int kernelMultitaskerShutdown(int nice)
{
  // This function will shut down the multitasker and halt the scheduler, 
  // returning exclusive control to the kernel process.  If the nice
  // argument is non-zero, this function will do a nice orderly shutdown,
  // killing all the running processes gracefully.  If it is zero, the
  // resources allocated to the processes will never be freed, and the
  // multitasker will just stop.  Returns 0 on success, negative otherwise.
 
  int status = 0;

  // Make sure multitasking has been enabled
  if (!multitaskingEnabled)
    // We can't yield if we're not multitasking yet
    return (status = ERR_NOTINITIALIZED);

  // If we are doing a "nice" shutdown, we will kill all the running
  // processes (except the kernel and scheduler) gracefully.
  if (nice)
    kernelMultitaskerKillAll();

  // Set the schedulerStop flag to stop the scheduler
  schedulerStop = 1;

  // Yield control back to the scheduler, so that it can stop
  kernelMultitaskerYield();

  // Make note that the multitasker has been disabled
  multitaskingEnabled = 0;

  // Deallocate the stack used by the scheduler
  kernelMemoryRelease(schedulerProc->userStack);

  // Print a message
  kernelLog("Multitasking stopped");
  
  return (status = 0);
}

/*
void kernelException(int num, unsigned address)
{
	// If we are already processing one, then it's a double-fault and we are
	// totally finished
	if (processingException)
	{
		kernelPanic("Double-fault (%s) while processing %s %s exception",
			exceptionVector[num].name,
			exceptionVector[processingException].a,
			exceptionVector[processingException].name);
	}

	processingException = num;
	exceptionAddress = address;

	// If there's a handler for this exception type, call it
	if (exceptionVector[processingException].handler
	&& (exceptionVector[processingException].handler() >= 0))
	{
		// The exception was handled.  Return to the caller.
		processingException = 0;
		exceptionAddress = 0;
		return;
	}

	// If multitasking is enabled, switch to the exception thread.  Otherwise
	// just call the exception handler as a function.
	if (multitaskingEnabled)
		kernelProcessorFarCall(exceptionProc->tssSelector);
	else
		exceptionHandler();

	// If the exception is handled, then this code is reached we return.
	return;
}
*/

void kernelException(int num, void* contex)
{
	// If we are already processing one, then it's a double-fault and we are
	// totally finished
	if (processingException)
	{
		kernelPanic("Double-fault (%s) while processing %s %s exception",
			exceptionVector[num].name,
			exceptionVector[processingException].a,
			exceptionVector[processingException].name);
	}

	processingException = num;
	exceptionAddress = ((kernelInterruptState*)contex)->eip;

	// If there's a handler for this exception type, call it
	if (exceptionVector[processingException].handler
	&& (exceptionVector[processingException].handler() >= 0))
	{
		// The exception was handled.  Return to the caller.
		processingException = 0;
		exceptionAddress = 0;
		return;
	}

	// If multitasking is enabled, switch to the exception thread.  Otherwise
	// just call the exception handler as a function.
	if (multitaskingEnabled)
		kernelProcessorFarCall(exceptionProc->tssSelector);
	else
		exceptionHandler();

	// If the exception is handled, then this code is reached we return.
	return;
}

void kernelMultitaskerDumpProcessList(void)
{
  // This routine is used to dump an internal listing of the current
  // process to the output.

  kernelTextOutputStream *currentOutput = NULL;
  kernelProcess *tmpProcess = NULL;
  char buffer[1024];
  int count;

  // Make sure multitasking has been enabled
  if (!multitaskingEnabled)
    return;

  // Get the current output stream
  currentOutput = kernelTextGetCurrentOutput();

  if (numQueued > 0)
    {
      kernelTextStreamPrintLine(currentOutput, "Process list:");

      for (count = 0; count < numQueued; count ++)
	{
	  tmpProcess = processQueue[count];
	  
	  sprintf(buffer, "\"%s\"  PID=%d UID=%d priority=%d "
		  "priv=%d parent=%d\n        %d%% CPU State=",
		  (char *) tmpProcess->processName,
		  tmpProcess->processId, tmpProcess->userId,
		  tmpProcess->priority, tmpProcess->privilege,
		  tmpProcess->parentProcessId, tmpProcess->cpuPercent);
	  // Get the state
	  switch(tmpProcess->state)
	    {
	    case proc_running:
	      strcat(buffer, "running");
	      break;
	    case proc_ready:
	      strcat(buffer, "ready");
	      break;
	    case proc_waiting:
	      strcat(buffer, "waiting");
	      break;
	    case proc_sleeping:
	      strcat(buffer, "sleeping");
	      break;
	    case proc_stopped:
	      strcat(buffer, "stopped");
	      break;
	    case proc_finished:
	      strcat(buffer, "finished");
	      break;
	    case proc_zombie:
	      strcat(buffer, "zombie");
	      break;
	    default:
	      strcat(buffer, "unknown");
	      break;
	    }
	  kernelTextStreamPrintLine(currentOutput, buffer);
	}
    }
  else
    // This doesn't seem at all likely.
    kernelTextStreamPrintLine(currentOutput, "No processes remaining");

  kernelTextStreamNewline(currentOutput);
  return;
}


int kernelMultitaskerGetProcess(int processId, process *userProcess)
{
	// Return the requested process.

	int status = 0;
	kernelProcess *kernProcess = NULL;

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		return (status = ERR_NOTINITIALIZED);
  
	// Check params
	if (userProcess == NULL)
		return (status = ERR_NULLPARAMETER);

	ACQUIRE_LOCK_PROCESS_QUEUE();

	// Try to match the requested process Id number with a real
	// live process structure
	kernProcess = getProcessById(processId);
	if (kernProcess == NULL)
	{
		// That means there's no such process
		RELEASE_LOCK_PROCESS_QUEUE();
		return (status = ERR_NOSUCHENTRY);
	}

	// Make it into a user space process
	kernelProcess2Process(kernProcess, userProcess);

	RELEASE_LOCK_PROCESS_QUEUE();

	return (status = 0);
}


int kernelMultitaskerGetProcessByName(const char *processName,
				      process *userProcess)
{
	// Return the requested process.

	int status = 0;
	kernelProcess *kernProcess = NULL;

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		return (status = ERR_NOTINITIALIZED);
  
	// Check params
	if ((processName == NULL) || (userProcess == NULL))
		return (status = ERR_NULLPARAMETER);

	ACQUIRE_LOCK_PROCESS_QUEUE();

	// Try to match the requested process Id number with a real
	// live process structure
	kernProcess = getProcessByName(processName);
	if (kernProcess == NULL)
	{
		// That means there's no such process
		RELEASE_LOCK_PROCESS_QUEUE();
		return (status = ERR_NOSUCHENTRY);
	}

	// Make it into a user space process
	kernelProcess2Process(kernProcess, userProcess);

	RELEASE_LOCK_PROCESS_QUEUE();

	return (status = 0);
}


int kernelMultitaskerGetProcesses(void *buffer, unsigned buffSize)
{
	// Return user-space process structures into the supplied buffer

	int status = 0;
	kernelProcess *kernProcess = NULL;
	process *userProcess = NULL;

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		return (status = ERR_NOTINITIALIZED);
  
	// Check params
	if (buffer == NULL)
		return (status = ERR_NULLPARAMETER);

	ACQUIRE_LOCK_PROCESS_QUEUE();

	for (status = 0; status < numQueued; status ++)
	{
		kernProcess = processQueue[status];
		userProcess = (buffer + (status * sizeof(process)));
		if ((void *) userProcess >= (buffer + buffSize))
			break;

		kernelProcess2Process(kernProcess, userProcess);
	}

	RELEASE_LOCK_PROCESS_QUEUE();

	return (status);
}


int kernelMultitaskerGetCurrentProcessId(void)
{
	// This is a very simple routine that can be called by external 
	// programs to get the PID of the current running process.  Of course,
	// internal functions can perform this action very easily themselves.

	int status = 0;

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		// If we're not multitasking,return the kernel's process Id
		return (status = KERNELPROCID);
  
	// Double-check the current process to make sure it's not NULL
	if (kernelCurrentProcess == NULL)
		return (status = ERR_NOSUCHPROCESS);

	// OK, we can return process Id of the currently running process
	return (status = kernelCurrentProcess->processId);
}


#include "kernelMultitasker_Stat.c"


int kernelMultitaskerGetProcessPrivilege(int processId)
{
	// This is a very simple routine that can be called by external 
	// programs to request the privilege of a "running" process.  Of course,
	// internal functions can perform this action very easily themselves.

	int status = 0;
	kernelProcess *theProcess;

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		return (status = ERR_NOTINITIALIZED);

	ACQUIRE_LOCK_PROCESS_QUEUE();

	// We need to find the process structure based on the process Id
	theProcess = getProcessById(processId);

	if (theProcess == NULL)
	{
		// The process does not exist
		RELEASE_LOCK_PROCESS_QUEUE();
		return (status = ERR_NOSUCHPROCESS);
	}

	status = theProcess->privilege;

	RELEASE_LOCK_PROCESS_QUEUE();

	// Return the nominal privilege value of the process
	return (status);
}

#include "kernelMultitasker_IO.c"

int kernelMultitaskerDispatch(int reason)
{
	// This routine will yield control from the current running thread
	// back to the scheduler.

	if (!multitaskingEnabled)
		return -1;

	if (kernelProcessingInterrupt)
		return -1;

	if (inScheduler)
		kernelPanic("Recursive dispatch call");
		//return -1;

	int oldProcessorLevel = kernelRaiseProcessorLevel(kernelProcessorLevelDispatch);

	kernelCurrentProcess->dispatchReason = reason;

	kernelLowerProcessorLevel(oldProcessorLevel);

	return 0;
}

int kernelMultitaskerYield(void)
{
	return kernelMultitaskerDispatch(DISPATCH_YIELD);
}

#include "kernelMultitasker_Priority.c"
#include "kernelMultitasker_Wait.c"
#include "kernelMultitasker_Kill.c"
#include "kernelMultitasker_Signal.c"
#include "kernelMultitasker_IOPerm.c"

kernelPageDirectory *kernelMultitaskerGetPageDir(int processId)
{
	kernelProcess *dirProcess = NULL;

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		return (NULL);

	ACQUIRE_LOCK_PROCESS_QUEUE();

	dirProcess = getProcessById(processId);
	if (dirProcess == NULL)
	{
		// There's no such process
		RELEASE_LOCK_PROCESS_QUEUE();
		kernelError(kernel_error, "No process %d to get page directory", processId);
		return (NULL);
	}

	kernelPageDirectory* pageDirectory = dirProcess->pageDirectory;

	RELEASE_LOCK_PROCESS_QUEUE();

	return (pageDirectory);
}
